#include "HttpResponse.h"
#include "Utils.h"
#include "ContentEncoding.h"

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <map>

static const RespFieldName s_RespFieldName[] = {
	HT_MAKE_RESP(RESP_NONE, ""),
	HT_MAKE_RESP(RESP_ACCEPT_RANGES, "Accept-Ranges"),
	HT_MAKE_RESP(RESP_AGE, "Age"),
	HT_MAKE_RESP(RESP_ALLOW, "Allow"),
	HT_MAKE_RESP(RESP_CACHE_CONTROL, "Cache-Control"),
    HT_MAKE_RESP(RESP_CONNECTION, "Connection"),
	HT_MAKE_RESP(RESP_CONTENT_BASE, "Content-Base"),
    HT_MAKE_RESP(RESP_CONTENT_ENCODING, "Content-Encoding"),
    HT_MAKE_RESP(RESP_CONTENT_LANGUAGE, "Content-Language"),
    HT_MAKE_RESP(RESP_CONTENT_LENGTH, "Content-Length"),
    HT_MAKE_RESP(RESP_CONTENT_LOCATION, "Content-Location"),
	HT_MAKE_RESP(RESP_CONTENT_MD5, "Content-MD5"),
    HT_MAKE_RESP(RESP_CONTENT_RANGE, "Content-Range"),
    HT_MAKE_RESP(RESP_CONTENT_TYPE, "Content-Type"), 
    HT_MAKE_RESP(RESP_DATE, "Date"),
	HT_MAKE_RESP(RESP_ETAG, "ETag"),
	HT_MAKE_RESP(RESP_EXPIRES, "Expires"),
	HT_MAKE_RESP(RESP_KEEP_ALIVE, "Keep-Alive"),
    HT_MAKE_RESP(RESP_LAST_MODIFIED, "Last-Modified"),  
	HT_MAKE_RESP(RESP_LINK, "Link"),
	HT_MAKE_RESP(RESP_LOCATION, "Location"),
	HT_MAKE_RESP(RESP_MIME_VER, "MIME-Version"),
	HT_MAKE_RESP(RESP_P3P, "P3P"),
    HT_MAKE_RESP(RESP_PRAGMA, "Pragma"),    
	HT_MAKE_RESP(RESP_PROXY_AUTH, "Proxy-Authenticate"),
	HT_MAKE_RESP(RESP_REFRESH, "Refresh"),
	HT_MAKE_RESP(RESP_RETRY_AFTER, "Retry-After"),
    HT_MAKE_RESP(RESP_SERVER, "Server"),
	HT_MAKE_RESP(RESP_SET_COOKIE, "Set-Cookie"),
	HT_MAKE_RESP(RESP_SET_COOKIE2, "Set-Cookie2"),
	HT_MAKE_RESP(RESP_TITLE, "Title"),
    HT_MAKE_RESP(RESP_TRAILER, "Trailer"),
    HT_MAKE_RESP(RESP_TRANSFER_ENCODING, "Transfer-Encoding"),
	HT_MAKE_RESP(RESP_UPGRADE, "Upgrade"),
    HT_MAKE_RESP(RESP_URI, "Uri"),
    HT_MAKE_RESP(RESP_VARY, "Vary"),
	HT_MAKE_RESP(RESP_VIA, "Via"),
	HT_MAKE_RESP(RESP_WARNING, "Warning"),
    HT_MAKE_RESP(RESP_WWW_AUTH, "WWW-Authenticate"),
	HT_MAKE_RESP(RESP_X_ACCELERATED_BY, "X-Accelerated-By"),
	HT_MAKE_RESP(RESP_X_ASPNET_VER, "X-AspNet-Version"),
	HT_MAKE_RESP(RESP_X_CACHE, "X-Cache"),
	HT_MAKE_RESP(RESP_X_CACHE_LOOKUP, "X-Cache-Lookup"),
	HT_MAKE_RESP(RESP_X_PAD, "X-Pad"),
	HT_MAKE_RESP(RESP_X_PING_BACK, "X-Pingback"),
	HT_MAKE_RESP(RESP_X_POWERED_BY, "X-Powered-By"),
	HT_MAKE_RESP(RESP_X_PSP, "X-PSP"),
	HT_MAKE_RESP(RESP_X_SERVER, "X-Server"),
	HT_MAKE_RESP(RESP_X_TIME, "X-Time"),
	HT_MAKE_RESP(RESP_X_UA_COMPAT, "X-UA-Compatible")
};

////////////////////////////////////////////////////////
//
class RespFieldMap : public map<string, HttpRespField> {
private:
	RespFieldMap();
public:
	static const RespFieldMap& instance();
	HttpRespField GetFieldType(const string& str) const;
};

RespFieldMap::RespFieldMap()
{
	int n = sizeof(s_RespFieldName)/sizeof(s_RespFieldName[0]);
	for(int i = 0; i < n; i++) {
		string name = s_RespFieldName[i].name;
		strtolower(name);
		(*this)[name] = s_RespFieldName[i].field;
	}
}

const RespFieldMap& RespFieldMap::instance()
{
	static RespFieldMap rspmap;
	return rspmap;
}

HttpRespField RespFieldMap::GetFieldType(const string& str) const
{
	RespFieldMap::const_iterator it = find(str);
	if(it != RespFieldMap::end())
		return it->second;

	return RESP_OTHER;
}

static const RespFieldMap& s_rspmap = RespFieldMap::instance();


////////////////////////////////////////////////////////
//
HttpResponse::HttpResponse()
{
}

HttpResponse::HttpResponse(const char *hbuf, int hlen)
{
	Parse(hbuf, hlen);
}

HttpResponse::~HttpResponse()
{
}

int  HttpResponse::QueryHttpVersion() const
{
	return m_version;
}

int  HttpResponse::QueryStatusCode() const
{
	return m_statuscode;
}

string HttpResponse::QueryStatusLine() const
{
	if(m_statuscode > 0)
		return string(m_rawhdr, m_fv[0].name.offset, m_fv[0].name.length);
	return string();
}

int  HttpResponse::GetHeaderLength() const
{
	return m_rawhdr.length();
}

string HttpResponse::GetRawHeader() const
{
	return m_rawhdr;
}

/** 
* Each header field consists of a name followed by a colon (":") and 
* the field value. Field names are case-insensitive. The field value 
* MAY be preceded by any amount of LWS, though a single SP is preferred. 
* Header fields can be extended over multiple lines by preceding each
* extra line with at least one SP or HT.
*
* Multiple message-header fields with the same field-name MAY be present 
* in a message if and only if the entire field-value for that header
* field is defined as a comma-separated list [i.e., #(values)]. It MUST be
* possible to combine the multiple header fields into one "field-name: field-value"
* pair, without changing the semantics of the message, by appending each 
* subsequent field-value to the first, each separated by a comma. The order
* in which header fields with the same field-name are received is therefore 
* significant to the interpretation of the combined field value, and thus a 
* proxy MUST NOT change the order of these field values when a message is forwarded. 
*/
#include <iostream>
using namespace std;
int HttpResponse::Parse(const char *hbuf, int hlen)
{
	m_fv.clear();
	m_rawhdr = "";
	m_statuscode = 0;
	m_version = 0;

	if(!hbuf || hlen<MIN_RESP_LEN)
		return 0;

	const char *p = hbuf, *pe = hbuf + hlen;
	/*Standard HTTP Response begin with HTTP/[version]*/
	if(memcmp(p, "HTTP/", 5) != 0) {
		return 0;
	}

	/* splits each line into an array */
	OStringRef strref(0, 0);
	bool bcontinue = true;
	while(bcontinue) {//
		while(p<pe && *p!='\n') p++;//find next LF
		if(p < pe) {
			p += 1;//next char
			size_t offset = p - hbuf;
			size_t length = offset - strref.offset;
			strref.length = length;
			m_fv.push_back(RespFieldValue(RESP_NONE, strref, OStringRef(0,0)));
			/*check if we have found an empty line*/
			if(length==1 || (length==2 && isspace(p[-2]))) {
				bcontinue = false;
			}
			strref.offset = offset;
		} else {
			//last line
			bcontinue = false;
			strref.length = p - hbuf - strref.offset;
			m_fv.push_back(RespFieldValue(RESP_NONE, strref, OStringRef(0,0)));
		}
	}

	//saving raw header
	int lineno = m_fv.size();
	m_rawhdr.append(hbuf, m_fv[lineno-1].name.offset + m_fv[lineno-1].name.length);

	/*parsing status code*/
	p = hbuf + 5; //strlen("HTTP/") = 5;
	pe = hbuf + m_fv[0].name.length; //field #0
	if(pe[-1]=='\n') { 
		pe--; m_fv[0].name.length--; 
		if(pe[-1]=='\r') { pe--; m_fv[0].name.length--; }
	}

	// find http version and status code
	if(p[1]=='.') {
		m_version = 100*(p[0]-'0') + (p[2]-'0');
		p += 3;
	}
	while(p<pe && *p!=' ') p++;
	while(p<pe && *p==' ') p++;
	if((pe - p) >= 4) {
		m_statuscode = 100*(p[0]-'0') + 10*(p[1]-'0') + (p[2]-'0');
	}

	/*parsing each line of response*/
	for(int i=1; i<lineno; i++) {
		const char *ps = hbuf + m_fv[i].name.offset;
		pe = ps + m_fv[i].name.length;
		/*parsing <Field: Value> pair*/
		for(p=ps; p<pe ; ++p) {
			if(*p == ':') {
				m_fv[i].name.length = p - ps;
				string name(ps, (p - ps));
				strtolower(name);
				//cout<<sname<<endl;
				m_fv[i].field = s_rspmap.GetFieldType(name);
				/*value*/
				p++;
				while(*p==' ' || *p =='\t') ++p;
				const char *q = pe - 1;
				while(*q=='\n' || *q=='\r') q--;
				int len = q - p + 1;
				if(len < 0) len = 0;
				m_fv[i].value.offset = p - hbuf;
				m_fv[i].value.length = len;
				break;
			}
		}//end for p
	}//end for i

	  cout << string(m_rawhdr,m_fv[0].name.offset,m_fv[0].name.length)<< endl;
	 	for( int j = 1; j < lineno; j++ ) {
	 		cout<<"#"<<j<<"["<<m_fv[j].field<<"]:";
	 		string n(m_rawhdr,m_fv[j].name.offset,m_fv[j].name.length);
	 		string v(m_rawhdr,m_fv[j].value.offset,m_fv[j].value.length);
			cout<<"["<<n<<"]"<<":["<<v<<"]"<<endl;
	 	}
	return m_rawhdr.length();
}

string HttpResponse::QueryField(HttpRespField field) const
{
	string value;
	for(size_t i=1; i<m_fv.size(); ++i) {
		if(m_fv[i].field == field) {
			if(!value.empty()) //Multiple fields with the same name
				value += ", ";
			value.append(m_rawhdr, m_fv[i].value.offset, m_fv[i].value.length);
		}
	}
	return value;
}


string HttpResponse::QueryField(const string& field) const
{
	string value;
	if(!field.empty()) {
		string name(field);
		strtolower(name);
		HttpRespField rf = s_rspmap.GetFieldType(name);
		if(rf != RESP_OTHER) {
			value = QueryField(rf);
		} else {
			for(size_t i=0; i<m_fv.size(); ++i) {
				if(m_fv[i].field==RESP_OTHER && name.length()==m_fv[i].name.length) {
					string fldname(m_rawhdr, m_fv[i].name.offset, m_fv[i].name.length);
					strtolower(fldname);
					if(fldname == name) {
						if(!value.empty()) //Multiple fields with the same name
							value += ", ";
						value.append(m_rawhdr, m_fv[i].value.offset, m_fv[i].value.length);
					}
				}
			} // END for
		} // END else
	}
	return value;
}

/**
 * refer to: http://www.io.com/~maus/HttpKeepAlive.html
 * HTTP/1.0
 *   Under HTTP 1.0, there is no official specification for how keepalive operates.
 * It was, in essence, tacked on to an existing protocol. If the browser supports
 * keep-alive, it adds an additional header to the request: 
 *     Connection: Keep-Alive
 * Then, when the server receives this request and generates a response, it also
 * adds a header to the response:
 *     Connection: Keep-Alive
 * Following this, the connection is NOT dropped, but is instead kept open. When
 * the client sends another request, it uses the same connection. This will continue 
 * until either the client or the server decides that the conversation is over, and 
 * one of them drops the connection.

 * HTTP/1.1
 * 	 Under HTTP 1.1, the official keepalive method is different. All connections
 * are kept alive, unless stated otherwise with the following header:
 *     Connection: close
 * The Connection: Keep-Alive header no longer has any meaning because of this.
 * Additionally, an optional Keep-Alive: header is described, but is so 
 * underspecified as to be meaningless. Avoid it.
 */

int  HttpResponse::QueryConnection() const
{
	string connection = QueryField(RESP_CONNECTION);

	if(!connection.empty()) {
		strtolower(connection);
		if(connection.find("close")!=string::npos) {
			return CONNECTION_CLOSE;
		} else if(connection.find("keep-alive")!=string::npos) {
			return CONNECTION_KEEP_ALIVE;
		}
	}

	if(m_version >= HTTPVER_1_1)
		return CONNECTION_KEEP_ALIVE;

	return CONNECTION_CLOSE;
}

int  HttpResponse::QueryContentEncoding() const
{
	string cntenc = QueryField(RESP_CONTENT_ENCODING);
	if(!cntenc.empty()) {
		strtolower(cntenc);
		if(cntenc == "gzip") 
			return CNT_ENCODE_GZIP;
		if(cntenc == "deflate")
			return CNT_ENCODE_DEFLATE;
	}
	return CNT_ENCODE_IDENTITY;
}

int  HttpResponse::QueryContentLength() const
{
	string cntlen = QueryField(RESP_CONTENT_LENGTH);
	if(!cntlen.empty()) {
		char *endptr = 0;
		int length = strtol(cntlen.c_str(), &endptr, 10);
		if(endptr && *endptr=='\0') {
			return length;
		} 
	} else if(!QueryField(RESP_TRANSFER_ENCODING).empty()) {
		return CNTLEN_CHUNKED;
	}

	return  CNTLEN_UNKNOWN;
}

string HttpResponse::QueryContentType() const
{
	return QueryField(RESP_CONTENT_TYPE);
}

string HttpResponse::QueryLocation() const
{
	return QueryField(RESP_LOCATION);
}

string HttpResponse::QueryCookie() const
{
	string cookie;
	for(size_t i=0; i<m_fv.size(); ++i) {
		if(m_fv[i].field==RESP_SET_COOKIE || m_fv[i].field==RESP_SET_COOKIE2) {
			if(!cookie.empty()) //Multiple fields with the same name
				cookie += "\n"; //
			cookie.append(m_rawhdr, m_fv[i].value.offset, m_fv[i].value.length);
		}
	}
	return cookie;
}


// EOF
